/* @flow */

import { warn } from 'core/util/index'
import { cached, isUndef } from 'shared/util'

//该方法用于处理直接创建Render函数时使用的事件修饰符
const normalizeEvent = cached((name: string): {
  name: string,
  once: boolean,
  capture: boolean,
  passive: boolean
} => {
  //这里解析'&~!'的顺序是在与解析模板字符串时设置的顺序相同吧？
  //是的，在模板解析时也是通过'&~!'的顺序记录修饰符的，详情可查看compiler/helper.js --> addHandler方法的源码
  //及其注释。
  const passive = name.charAt(0) === '&'
  name = passive ? name.slice(1) : name
  const once = name.charAt(0) === '~' // Prefixed last, checked first
  name = once ? name.slice(1) : name
  const capture = name.charAt(0) === '!'
  name = capture ? name.slice(1) : name
  return {
    name,
    once,
    capture,
    passive
  }
})

//该方法用于创建一个函数，该函数存在一个fns属性，该属性指向一个或多个回调函数，
//在该函数内部只是简单的运行这些回调函数
export function createFnInvoker (fns: Function | Array<Function>): Function {
  function invoker () {
    //什么情况下会向fns数组中添加回调函数？
    //比如在mergeVNodeHook方法中会向fns数组中添加回调函数
    const fns = invoker.fns
    if (Array.isArray(fns)) {
      const cloned = fns.slice()
      for (let i = 0; i < cloned.length; i++) {
        cloned[i].apply(null, arguments)
      }
    } else {
      // return handler return value for single handlers
      return fns.apply(null, arguments)
    }
  }
  invoker.fns = fns
  return invoker
}

//on是新的事件对象，oldOn是之前的事件对象。该方法用于根据旧的事件对象中
//添加的事件与新的事件对象进行比较，从而对事件进行增、删、改。
//注意：所有的监听函数都需要创建相应的代理函数进行代理
export function updateListeners (
  on: Object,
  oldOn: Object,
  add: Function,
  remove: Function,
  vm: Component
) {
  let name, cur, old, event
  for (name in on) {
    cur = on[name] //这里的cur函数已经通过bind方法绑定了上下文
    old = oldOn[name]
    //event是对象，属性分别为：{name:'', once:Boolean, capture:Boolean, passive:Boolean}
    event = normalizeEvent(name)
    if (isUndef(cur)) {
      process.env.NODE_ENV !== 'production' && warn(
        `Invalid handler for event "${event.name}": got ` + String(cur),
        vm
      )
    } else if (isUndef(old)) {//如果旧的事件对象中不存在当前事件，则添加。
      if (isUndef(cur.fns)) {
        cur = on[name] = createFnInvoker(cur) // 创建一个代理函数，在代理函数内部调用目标函数
      }
      add(event.name, cur, event.once, event.capture, event.passive)
    } else if (cur !== old) {//如果旧的事件对象中的事件函数与新的不同，则替换。
      old.fns = cur
      on[name] = old
    }
  }
  for (name in oldOn) {
    if (isUndef(on[name])) {//如果旧的事件对象中的函数在新的对象中不存在，则删除。
      event = normalizeEvent(name)
      remove(event.name, oldOn[name], event.capture)
    }
  }
}
